home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / LANG / C / GCC / V2-4-5 / GPPLIBSR00 / cc / filebuf < prev    next >
Text File  |  1993-12-07  |  15KB  |  582 lines

  1. //    This is part of the iostream library, providing input/output for C++.
  2. //    Copyright (C) 1991, 1992 Per Bothner.
  3. //
  4. //    This library is free software; you can redistribute it and/or
  5. //    modify it under the terms of the GNU Library General Public
  6. //    License as published by the Free Software Foundation; either
  7. //    version 2 of the License, or (at your option) any later version.
  8. //
  9. //    This library is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. //    Library General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU Library General Public
  15. //    License along with this library; if not, write to the Free
  16. //    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "ioprivate.h"
  19. extern "C"
  20.   {
  21.   #include <sys/types.h>
  22.   #include <sys/stat.h>
  23.   #include <fcntl.h>
  24.   #include <errno.h>
  25.   }
  26.  
  27.  
  28. // An fstream can be in at most one of put mode, get mode, or putback mode.
  29. // Putback mode is a variant of get mode.
  30.  
  31. // In a filebuf, there is only one current position, instead of two
  32. // separate get and put pointers.  In get mode, the current posistion
  33. // is that of gptr(); in put mode that of pptr().
  34.  
  35. // The position in the buffer that corresponds to the position
  36. // in external file system is file_ptr().
  37. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  38. // If the field _fb._offset is >= 0, it gives the offset in
  39. // the file as a whole corresponding to eGptr(). (???)
  40.  
  41. // PUT MODE:
  42. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  43. // Also, epptr() == ebuf().
  44. // Also, eback() == gptr() && gptr() == egptr().
  45. // The un-flushed character are those between pbase() and pptr().
  46. // GET MODE:
  47. // If a filebuf is in get or putback mode, eback() != egptr().
  48. // In get mode, the unread characters are between gptr() and egptr().
  49. // The OS file position corresponds to that of egptr().
  50. // PUTBACK MODE:
  51. // Putback mode is used to remember "excess" characters that have
  52. // been sputbackc'd in a separate putback buffer.
  53. // In putback mode, the get buffer points to the special putback buffer.
  54. // The unread characters are the characters between gptr() and egptr()
  55. // in the putback buffer, as well as the area between save_gptr()
  56. // and save_egptr(), which point into the original reserve buffer.
  57. // (The pointers save_gptr() and save_egptr() are the values
  58. // of gptr() and egptr() at the time putback mode was entered.)
  59. // The OS position corresponds to that of save_egptr().
  60. //
  61. // LINE BUFFERED OUTPUT:
  62. // During line buffered output, pbase()==base() && epptr()==base().
  63. // However, ptr() may be anywhere between base() and ebuf().
  64. // This forces a call to filebuf::overflow(int C) on every put.
  65. // If there is more space in the buffer, and C is not a '\n',
  66. // then C is inserted, and pptr() incremented.
  67. //
  68. // UNBUFFERED STREAMS:
  69. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  70.  
  71. #define CLOSED_FILEBUF_FLAGS \
  72.   (_S_IS_FILEBUF+_S_NO_READS+_S_NO_WRITES+_S_TIED_PUT_GET)
  73.  
  74. void filebuf::init()
  75. {
  76.     _fb._offset = 0;
  77.  
  78.     _link_in();
  79.     _fb._fileno = -1;
  80. }
  81.  
  82. filebuf::filebuf() : backupbuf(CLOSED_FILEBUF_FLAGS)
  83. {
  84.     init();
  85. }
  86.  
  87. filebuf::filebuf(int fd) : backupbuf(CLOSED_FILEBUF_FLAGS)
  88. {
  89.     init();
  90.     attach(fd);
  91. }
  92.  
  93. filebuf::filebuf(int fd, char* p, int len) : backupbuf(CLOSED_FILEBUF_FLAGS)
  94. {
  95.     init();
  96.     attach(fd);
  97.     setbuf(p, len);
  98. }
  99.  
  100. filebuf::~filebuf()
  101. {
  102.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  103.     close();
  104.  
  105.     _un_link();
  106. }
  107.  
  108. filebuf* filebuf::open(const char *filename, ios::openmode mode, int prot)
  109. {
  110.     if (is_open())
  111.     return NULL;
  112.     int posix_mode;
  113.     int read_write;
  114.     if (mode & ios::app)
  115.     mode |= ios::out;
  116.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out)) {
  117.     posix_mode = O_RDWR;
  118.     read_write = 0;
  119.     }
  120.     else if (mode & ios::out)
  121.     posix_mode = O_WRONLY, read_write = _S_NO_READS;
  122.     else if (mode & (int)ios::in)
  123.     posix_mode = O_RDONLY, read_write = _S_NO_WRITES;
  124.     else
  125.     posix_mode = 0, read_write = _S_NO_READS+_S_NO_WRITES;
  126.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  127.     posix_mode |= O_TRUNC;
  128.     if (mode & ios::app)
  129.     posix_mode |= O_APPEND, read_write |= _S_IS_APPENDING;
  130.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  131.     posix_mode |= O_CREAT;
  132.     if (mode & (int)ios::noreplace)
  133.     posix_mode |= O_EXCL;
  134.     int fd = ::open(filename, posix_mode, prot);
  135.     if (fd < 0)
  136.     return NULL;
  137.     _fb._fileno = fd;
  138.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  139.     if (mode & (ios::ate|ios::app)) {
  140.     if (seekoff(0, ios::end) == EOF)
  141.         return NULL;
  142.     }
  143.     _link_in();
  144.     return this;
  145. }
  146.  
  147. filebuf* filebuf::open(const char *filename, const char *mode)
  148. {
  149.     if (is_open())
  150.     return NULL;
  151.     int oflags = 0, omode;
  152.     int read_write;
  153.     int oprot = 0666;
  154.     switch (*mode++) {
  155.       case 'r':
  156.     omode = O_RDONLY;
  157.     read_write = _S_NO_WRITES;
  158.     break;
  159.       case 'w':
  160.     omode = O_WRONLY;
  161.     oflags = O_CREAT|O_TRUNC;
  162.     read_write = _S_NO_READS;
  163.     break;
  164.       case 'a':
  165.     omode = O_WRONLY;
  166.     oflags = O_CREAT|O_APPEND;
  167.     read_write = _S_NO_READS|_S_IS_APPENDING;
  168.     break;
  169.       default:
  170.     errno = EINVAL;
  171.     return NULL;
  172.     }
  173.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  174.     omode = O_RDWR;
  175.     read_write &= _S_IS_APPENDING;
  176.     }
  177.     int fdesc = ::open(filename, omode|oflags, oprot);
  178.     if (fdesc < 0)
  179.     return NULL;
  180.     _fb._fileno = fdesc;
  181.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  182.     if (read_write & _S_IS_APPENDING)
  183.     if (seekoff(0, ios::end) == EOF)
  184.         return NULL;
  185.     _link_in();
  186.     return this;
  187. }
  188.  
  189. filebuf* filebuf::attach(int fd)
  190. {
  191.     if (is_open())
  192.     return NULL;
  193.     _fb._fileno = fd;
  194.     xsetflags(0, _S_NO_READS+_S_NO_WRITES);
  195.     return this;
  196. }
  197.  
  198. streambuf* filebuf::setbuf(char* p, int len)
  199. {
  200.     if (streambuf::setbuf(p, len) == NULL)
  201.     return NULL;
  202.     setp(_base, _base);
  203.     setg(_base, _base, _base);
  204.     return this;
  205. }
  206.  
  207. int filebuf::overflow(int c)
  208. {
  209.     if (xflags() & _S_NO_WRITES) // SET ERROR
  210.     return EOF;
  211.     // Allocate a buffer if needed.
  212.     if (base() == NULL) {
  213.     doallocbuf();
  214.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(_base, _base);
  215.     else setp(_base, _ebuf);
  216.     setg(_base, _base, _base);
  217.     _flags |= _S_CURRENTLY_PUTTING;
  218.     }
  219.     // If currently reading, switch to writing.
  220.     else if ((_flags & _S_CURRENTLY_PUTTING) == 0) {
  221.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(gptr(), gptr());
  222.     else setp(gptr(), ebuf());
  223.     setg(egptr(), egptr(), egptr());
  224.     _flags |= _S_CURRENTLY_PUTTING;
  225.     }
  226.     if (c == EOF)
  227.     return do_flush();
  228.     if (pptr() == ebuf() ) // Buffer is really full
  229.     if (do_flush() == EOF)
  230.         return EOF;
  231.     xput_char(c);
  232.     if (unbuffered() || (linebuffered() && c == '\n'))
  233.     if (do_flush() == EOF)
  234.         return EOF;
  235.     return (unsigned char)c;
  236. }
  237.  
  238. int filebuf::underflow()
  239. {
  240. #if 0
  241.     /* SysV does not make this test; take it out for compatibility */
  242.     if (fp->_flags & __SEOF)
  243.     return (EOF);
  244. #endif
  245.  
  246.     if (xflags() & _S_NO_READS)
  247.     return EOF;
  248.     if (gptr() < egptr())
  249.     return *(unsigned char*)gptr();
  250.     allocbuf();
  251.  
  252.     // FIXME This can/should be moved to __streambuf ??
  253.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  254.     // Flush all line buffered files before reading.
  255.     streambuf::flush_all_linebuffered();
  256.     }
  257.  
  258.     switch_to_get_mode();
  259.  
  260.     _G_ssize_t count = sys_read(base(), ebuf() - base());
  261.     if (count <= 0) {
  262.     if (count == 0)
  263.         xsetflags(_S_EOF_SEEN);
  264.     else
  265.         xsetflags(_S_ERR_SEEN), count = 0;
  266.     }
  267.     setg(base(), base(), base() + count);
  268.     setp(base(), base());
  269.     if (count == 0)
  270.     return EOF;
  271.     if (_fb._offset >= 0)
  272.     _fb._offset += count;
  273.     return *(unsigned char*)gptr();
  274. }
  275.  
  276. int filebuf::do_write(const char *data, int to_do)
  277. {
  278.     if (to_do == 0)
  279.     return 0;
  280.     if (xflags() & _S_IS_APPENDING) {
  281.     // On a system without a proper O_APPEND implementation,
  282.     // you would need to sys_seek(0, ios::end) here, but is
  283.     // is not needed nor desirable for Unix- or Posix-like systems.
  284.     // Instead, just indicate that offset (before and after) is
  285.     // unpredictable.
  286.     _fb._offset = -1;
  287.     }
  288.     else if (egptr() != pbase()) {
  289.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  290.     if (new_pos == -1)
  291.         return EOF;
  292.     _fb._offset = new_pos;
  293.     }
  294.     _G_ssize_t count = sys_write(data, to_do);
  295.     if (_cur_column)
  296.     _cur_column = __adjust_column(_cur_column - 1, data, to_do) + 1;
  297.     setg(base(), base(), base());
  298.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(base(), base());
  299.     else setp(base(), ebuf());
  300.     return count != to_do ? EOF : 0;
  301. }
  302.  
  303. int filebuf::sync()
  304. {
  305. //    char* ptr = cur_ptr();
  306.     if (pptr() > pbase())
  307.     if (do_flush()) return EOF;
  308.     if (gptr() != egptr()) {
  309.     streampos delta = gptr() - egptr();
  310.     if (in_backup())
  311.         delta -= eGptr() - Gbase();
  312.     if (sys_seek(delta, ios::cur) == EOF)
  313.         return EOF;
  314.     }
  315.     // FIXME: Cleanup - can this be shared?
  316. //    setg(base(), ptr, ptr);
  317.     return 0;
  318. }
  319.  
  320. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  321. {
  322.     streampos result, new_offset, delta;
  323.     _G_ssize_t count;
  324.  
  325.     if (mode == 0) // Don't move any pointers.
  326.     dir = ios::cur, offset = 0;
  327.  
  328.     // Flush unwritten characters.
  329.     // (This may do an unneeded write if we seek within the buffer.
  330.     // But to be able to switch to reading, we would need to set
  331.     // egptr to ptr.  That can't be done in the current design,
  332.     // which assumes file_ptr() is eGptr.  Anyway, since we probably
  333.     // end up flushing when we close(), it doesn't make much difference.)
  334.     if (pptr() > pbase() || put_mode())
  335.     if (switch_to_get_mode()) return EOF;
  336.  
  337.     if (base() == NULL) {
  338.     doallocbuf();
  339.     setp(base(), base());
  340.     setg(base(), base(), base());
  341.     }
  342.     switch (dir) {
  343.       case ios::cur:
  344.     if (_fb._offset < 0) {
  345.         _fb._offset = sys_seek(0, ios::cur);
  346.         if (_fb._offset < 0)
  347.         return EOF;
  348.     }
  349.     // Make offset absolute, assuming current pointer is file_ptr().
  350.     offset += _fb._offset;
  351.  
  352.     offset -= _egptr - _gptr;
  353.     if (in_backup())
  354.         offset -= _other_egptr - _other_gbase;
  355.     dir = ios::beg;
  356.     break;
  357.       case ios::beg:
  358.     break;
  359.       case ios::end:
  360.     struct stat st;
  361.     if (sys_stat(&st) == 0 && S_ISREG(st.st_mode)) {
  362.         offset += st.st_size;
  363.         dir = ios::beg;
  364.     }
  365.     else
  366.         goto dumb;
  367.     }
  368.     // At this point, dir==ios::beg.
  369.  
  370.     // If destination is within current buffer, optimize:
  371.     if (_fb._offset >= 0 && _eback != NULL) {
  372.     // Offset relative to start of main get area.
  373.     _G_fpos_t rel_offset = offset - _fb._offset
  374.         + (eGptr()-Gbase());
  375.     if (rel_offset >= 0) {
  376.         if (in_backup())
  377.         switch_to_main_get_area();
  378.         if (rel_offset <= _egptr - _eback) {
  379.         setg(base(), base() + rel_offset, egptr());
  380.         setp(base(), base());
  381.         return offset;
  382.         }
  383.         // If we have streammarkers, seek forward by reading ahead.
  384.         if (have_markers()) {
  385.         int to_skip = rel_offset - (_gptr - _eback);
  386.         if (ignore(to_skip) != to_skip)
  387.             goto dumb;
  388.         return offset;
  389.         }
  390.     }
  391.     if (rel_offset < 0 && rel_offset >= Bbase() - Bptr()) {
  392.         if (!in_backup())
  393.         switch_to_backup_area();
  394.         gbump(_egptr + rel_offset - gptr());
  395.         return offset;
  396.     }
  397.     }
  398.  
  399.     unsave_markers();
  400.  
  401.     // Try to seek to a block boundary, to improve kernel page management.
  402.     new_offset = offset & ~(ebuf() - base() - 1);
  403.     delta = offset - new_offset;
  404.     if (delta > ebuf() - base()) {
  405.     new_offset = offset;
  406.     delta = 0;
  407.     }
  408.     result = sys_seek(new_offset, ios::beg);
  409.     if (result < 0)
  410.     return EOF;
  411.     if (delta == 0)
  412.     count = 0;
  413.     else {
  414.     count = sys_read(base(), ebuf()-base());
  415.     if (count < delta) {
  416.         // We weren't allowed to read, but try to seek the remainder.
  417.         offset = count == EOF ? delta : delta-count;
  418.         dir = ios::cur;
  419.         goto dumb;
  420.     }
  421.     }
  422.     setg(base(), base()+delta, base()+count);
  423.     setp(base(), base());
  424.     _fb._offset = result + count;
  425.     xflags(xflags() & ~ _S_EOF_SEEN);
  426.     return offset;
  427.   dumb:
  428.     unsave_markers();
  429.     result = sys_seek(offset, dir);
  430.     if (result != EOF) {
  431.     xflags(xflags() & ~_S_EOF_SEEN);
  432.     }
  433.     _fb._offset = result;
  434.     setg(base(), base(), base());
  435.     setp(base(), base());
  436.     return result;
  437. }
  438.  
  439. filebuf* filebuf::close()
  440. {
  441.     if (!is_open())
  442.     return NULL;
  443.  
  444.     // This flushes as well as switching mode.
  445.     if (pptr() > pbase() || put_mode())
  446.     if (switch_to_get_mode()) return NULL;
  447.  
  448.     unsave_markers();
  449.  
  450.     int status = sys_close();
  451.  
  452.     // Free buffer.
  453.     setb(NULL, NULL, 0);
  454.     setg(NULL, NULL, NULL);
  455.     setp(NULL, NULL);
  456.  
  457.     _un_link();
  458.     _flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
  459.     _fb._fileno = EOF;
  460.     _fb._offset = 0;
  461.  
  462.     return status < 0 ? NULL : this;
  463. }
  464.  
  465. _G_ssize_t filebuf::sys_read(char* buf, size_t size)
  466. {
  467.     for (;;) {
  468.     _G_ssize_t count = ::read(_fb._fileno, buf, size);
  469.     if (count != -1 || errno != EINTR)
  470.         return count;
  471.     }
  472. }
  473.  
  474. _G_fpos_t filebuf::sys_seek(_G_fpos_t offset, _seek_dir dir)
  475. {
  476.     return ::lseek(fd(), offset, (int)dir);
  477. }
  478.  
  479. _G_ssize_t filebuf::sys_write(const void *buf, long n)
  480. {
  481.     long to_do = n;
  482.     while (to_do > 0) {
  483.     _G_ssize_t count = ::write(fd(), buf, to_do);
  484.     if (count == EOF) {
  485.         if (errno == EINTR)
  486.         continue;
  487.         else {
  488.         _flags |= _S_ERR_SEEN;
  489.         break;
  490.         }
  491.     }
  492.     to_do -= count;
  493.     buf = (void*)((char*)buf + count);
  494.     }
  495.     n -= to_do;
  496.     if (_fb._offset >= 0)
  497.     _fb._offset += n;
  498.     return n;
  499. }
  500.  
  501. int filebuf::sys_stat(void* st)
  502. {
  503.     return ::_fstat(fd(), (struct stat*)st);
  504. }
  505.  
  506. int filebuf::sys_close()
  507. {
  508.     return ::close(fd());
  509. }
  510.  
  511. int filebuf::xsputn(const char *s, int n)
  512. {
  513.     if (n <= 0)
  514.     return 0;
  515.     // This is an optimized implementation.
  516.     // If the amount to be written straddles a block boundary
  517.     // (or the filebuf is unbuffered), use sys_write directly.
  518.  
  519.     int to_do = n;
  520.     int must_flush = 0;
  521.     // First figure out how much space is available in the buffer.
  522.     int count = _epptr - _pptr; // Space available.
  523.     if (linebuffered() && (_flags & _S_CURRENTLY_PUTTING)) {
  524.     count =_ebuf - _pptr;
  525.     if (count >= n) {
  526.         for (register const char *p = s + n; p > s; ) {
  527.         if (*--p == '\n') {
  528.             count = p - s + 1;
  529.             must_flush = 1;
  530.             break;
  531.         }
  532.         }
  533.     }
  534.     }
  535.     // Then fill the buffer.
  536.     if (count > 0) {
  537.     if (count > to_do)
  538.         count = to_do;
  539.     if (count > 20) {
  540.         memcpy(pptr(), s, count);
  541.         s += count;
  542.     }
  543.     else {
  544.         register char *p = pptr();;
  545.         for (register int i = count; --i >= 0; ) *p++ = *s++;
  546.     }
  547.     pbump(count);
  548.     to_do -= count;
  549.     }
  550.     if (to_do + must_flush > 0) {
  551.     // Next flush the (full) buffer.
  552.     if (__overflow(this, EOF) == EOF)
  553.         return n - to_do;
  554.  
  555.     // Try to maintain alignment: write a whole number of blocks.
  556.     // dont_write is what gets left over.
  557.     int block_size = _ebuf - _base;
  558.     int dont_write = block_size >= 128 ? to_do % block_size : 0;
  559.  
  560.     _G_ssize_t count = to_do - dont_write;
  561.     if (do_write(s, count) == EOF)
  562.         return n - to_do;
  563.     to_do = dont_write;
  564.  
  565.     // Now write out the remainder.  Normally, this will fit in the
  566.     // buffer, but it's somewhat messier for line-buffered files,
  567.     // so we let streambuf::sputn handle the general case.
  568.     if (dont_write)
  569.         to_do -= streambuf::sputn(s+count, dont_write);
  570.     }
  571.     return n - to_do;
  572. }
  573.  
  574. int filebuf::xsgetn(char *s, int n)
  575. {
  576.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  577.     return streambuf::xsgetn(s, n);
  578. }
  579.  
  580. // Non-ANSI AT&T-ism:  Default open protection.
  581. const int filebuf::openprot = 0644;
  582.